package wsi.script;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.rygh.core.util.UIo;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlParser;
import org.rygh.core.xml.XmlPath;
import org.rygh.core.xml.XmlTree;
import org.rygh.semantic.jena.SDatabase;
import org.rygh.semantic.model.rdf.Entity;
import org.rygh.semantic.model.rdf.Predicate;
import org.rygh.semantic.sparql.QueryComposer;
import org.rygh.semantic.sparql.ResultList;
import org.rygh.semantic.sparql.SparQL;

import wsi.task.Engine.InputSelectType;

public abstract class AppXml {
	public final static String DEFAULT_FILE = "/home/nanmus/Experiments/app/config_gui.xml";
	public final static boolean __DEBUG = true;
	public static boolean __DEBUG_APPEND_RERUN = false;

	// this is used for building xml only, not runtime.
	public final static String YAGO = "yago";
	public final static String MBZ = "mbz";
	public final static String IMDB = "imdb";

	protected final String filePath;
	protected final Map<String, QueryTuple> queryMap = new HashMap<>();

	protected XmlTree tree;

	protected AppXml() {
		this(DEFAULT_FILE);
	}

	protected AppXml(String filePath) {
		this.filePath = filePath;

		if (filePath != null)
			load();
	}

	protected boolean checkQueries() {
		System.out.println("checking queries");

		List<XmlLeaf> queryNodes = tree.getNodesByXPath(XmlPath.compose(
				AppTag.application, AppTag.configuration, AppTag.queries,
				AppTag.input_query));

		for (XmlLeaf node : queryNodes) {
			XmlObject obj = node.getValue();

			QueryComposer comp = new QueryComposer(obj.getValue());
			SparQL query = comp.compose();
			query.distinct("?x");

			ResultList res = SDatabase.shared().select(query);
			assert res != null : query;
			assert res.size() > 60 : query;

			if (res.size() < 100) {
				System.out.printf("query %s has a small size %d\n",
						obj.getAttribute(AppTag.id), res.size());
			}
		}

		return true;
	}

	private String getAttribute(AppTag attribute, AppTag property,
			String value, AppTag... tags) {
		XmlObject obj = getObject(property, value, tags);

		if (obj != null)
			return obj.getAttribute(attribute);
		else
			return null;
	}

	protected File getCacheFolder() {
		return new File(getValue(AppTag.application, AppTag.configuration,
				AppTag.locations, AppTag.cache));
	}

	protected File getDatabaseFolder() {
		return new File(getValue(AppTag.application, AppTag.configuration,
				AppTag.locations, AppTag.kdb));
	}

	protected int getDegreeLimit() {
		return Integer.parseInt(getValue(AppTag.application,
				AppTag.configuration, AppTag.parameters, AppTag.degree_limit));
	}

	protected String getDomain() {
		return getAttribute(AppTag.domain, AppTag.name, getKnowledgeDatabase(),
				AppTag.application, AppTag.configuration, AppTag.parameters,
				AppTag.databases, AppTag.kdb);
	}

	protected double getFunctionalityThreshold() {
		return Double.parseDouble(getAttribute(AppTag.functionality_threshold,
				AppTag.name, getKnowledgeDatabase(), AppTag.application,
				AppTag.configuration, AppTag.parameters, AppTag.databases,
				AppTag.kdb));
	}

	protected File getGoldSetFolder() {
		return new File(getValue(AppTag.application, AppTag.configuration,
				AppTag.locations, AppTag.gold_set));
	}

	protected int getHop() {
		return Integer.parseInt(getValue(AppTag.application,
				AppTag.configuration, AppTag.parameters, AppTag.hop));
	}

	protected InputSelectType getInputSelectType() {
		return InputSelectType
				.valueOf(getValue(AppTag.application, AppTag.configuration,
						AppTag.parameters, AppTag.input_selection));
	}

	protected int getInputSize() {
		return Integer.parseInt(getValue(AppTag.application,
				AppTag.configuration, AppTag.parameters, AppTag.input_size));
	}

	protected String getJena() {
		return getValue(AppTag.application, AppTag.configuration,
				AppTag.parameters, AppTag.jena);
	}

	protected String getKnowledgeDatabase() {
		return getValue(AppTag.application, AppTag.configuration,
				AppTag.parameters, AppTag.kdb);
	}

	private XmlObject getObject(AppTag property, String value, AppTag... tags) {
		List<XmlLeaf> nodes = tree.getNodesByXPath(XmlPath.compose(tags));

		for (XmlLeaf n : nodes) {
			XmlObject obj = n.getValue();

			if (obj.getAttribute(property).equals(value))
				return obj;
		}

		return null;
	}

	public Map<String, QueryTuple> getQueryMap() {
		return queryMap;
	}

	protected String getRelationDatabase() {
		return getValue(AppTag.application, AppTag.configuration,
				AppTag.parameters, AppTag.rdb);
	}

	protected double getResolutionThreshold() {
		return Double.parseDouble(getValue(AppTag.application,
				AppTag.configuration, AppTag.parameters,
				AppTag.resolution_threshold));
	}

	protected File getResourcesFolder() {
		return new File(getValue(AppTag.application, AppTag.configuration,
				AppTag.locations, AppTag.resources));
	}

	protected File getRunsFolder() {
		return new File(getValue(AppTag.application, AppTag.configuration,
				AppTag.locations, AppTag.runs));
	}

	private String getValue(AppTag... tags) {
		XmlLeaf node = tree.getNodeByXPath(XmlPath.compose(tags));

		return node.getValue().getValue();
	}

	String getValue(AppTag property, String value, AppTag... tags) {
		XmlObject obj = getObject(property, value, tags);

		if (obj != null)
			return obj.getValue();
		else
			return null;
	}

	protected boolean hasTree() {
		return tree != null;
	}

	protected boolean isWebCacheOn() {
		return Boolean.parseBoolean(getValue(AppTag.application,
				AppTag.configuration, AppTag.parameters, AppTag.web_cache_on));
	}

	private void load() {
		File file = new File(filePath);

		if (!file.exists())
			return;

		String content = UIo.readFile(file);

		XmlParser parser = new XmlParser(content);
		tree = parser.parse();
	}

	protected void loadQueries() {
		List<XmlLeaf> queryNodes = tree.getNodesByXPath(XmlPath.compose(
				AppTag.application, AppTag.configuration, AppTag.queries));

		String db = getKnowledgeDatabase();

		for (XmlLeaf node : queryNodes) {
			XmlObject obj = node.getValue();

			String db_name = obj.getAttribute(AppTag.kdb);

			if (!db.equals(db_name))
				continue;

			for (XmlLeaf child : node.getChildren()) {
				obj = child.getValue();

				Entity type;
				String key, smax, stype;

				key = obj.getAttribute(AppTag.id);
				assert !queryMap.containsKey(key);

				smax = obj.getAttribute(AppTag.max);
				int degree_max = smax == null ? -1 : Integer.parseInt(smax);

				stype = obj.getAttribute(AppTag.type);
				type = stype == null ? null : new Entity(stype);

				QueryComposer comp = new QueryComposer(obj.getValue());
				SparQL query = comp.compose();
				// query.distinct("?x");

				Predicate name_pred = new Predicate(
						obj.getAttribute(AppTag.name_predicate));

				String ls = obj.getAttribute(AppTag.link_predicate);
				Predicate link_pred = null;

				if (ls != null)
					link_pred = new Predicate(ls);

				QueryTuple t = new QueryTuple(key);
				t.setQuery(query);
				t.setType(type);
				t.setMaxDegree(degree_max);
				t.setNamePredicate(name_pred);
				t.setLinkPredicate(link_pred);

				// System.out
				// .printf("[info] %s -> name_pred %s\n", key, name_pred);

				queryMap.put(key, t);
			}
		}

	}

	public void save() {
		UIo.saveFile(new File(filePath), tree.toXmlString());
	}

	public void setTree(XmlTree tree) {
		this.tree = tree;
	}

}
